%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Industrial Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% createMissingDirs.m
% ffmpeg
%
%==========================================================================

%==========================================================================
% The function "getImagesFromMovie" extracts specified ranges of frames
% from a given movie and stores them to grayscale image files of desired
% format and file name pattern.
% INPUT: "movieFileName" is an input movie file from which frames are to be
% extracted.
% "frameRate" is the frame rate of the given movie.
% "frameRangesArr" is a 2D array of two columns and many rows. Each row
% represents a range of frames to be extracted.
% "imageFileNamePrefix" is a string specifying a common prefix (including a
% full directory path) for the image files where the requested frames are
% to be stored.
% "indFieldLength" is an optional parameter specifying the number of digits
% used to specify the index of a image in its file name. Image indices are
% specified with leading zeros. For example, if the index field length is
% 6, then the index of image 241 is specified as 000241 in its file name.
% "imageFileExt" is an optional string specifying a format for the image
% files where the requested frames are to be stored.
% "framesPerBatch' is an optional integer specifying the number of frames
% per batch. Frames are extracted, written to image files, and those files
% are renamed. This process is run in batches to minimize the loss if it is
% user-interrupted at some point and has to be restarted later.
% OUTPUT: None.
%==========================================================================
function getImagesFromMovie(movieFileName, frameRate, frameRangesArr, ...
    imageFileNamePrefix, indFieldLength, imageFileExt, framesPerBatch)

if (nargin < 5)
    indFieldLength = 6;
end
if (nargin < 6)
    imageFileExt = 'pgm';
end
if (nargin < 7)
    framesPerBatch = 200;
end

% Create any missing directories.
createMissingDirs(imageFileNamePrefix);
    
% Construct a file name pattern for the new image files.
ffmpegImageFileNamePattern = ['"' imageFileNamePrefix 'Ffmpeg_%0' num2str(indFieldLength) 'd.' imageFileExt '"'];

% Set up the format string for the image files' index field.
formatStr = ['%0' num2str(indFieldLength) 'd'];

% Extract and store to image files all frames in the new subranges of the
% requested range.
for ind = 1:size(frameRangesArr, 1)
    
    % Compute total number of frames in the current subrange.
    numRequestedFrames = frameRangesArr(ind, 2) - frameRangesArr(ind, 1) + 1;
    
    % Keep track of the index of the frame before the first frame in the
    % current batch.
    oneBeforeFirstInCurrBatch = frameRangesArr(ind, 1) - 1;
    % Keep track of the number of frames from the current subrange that are
    % not processed yet.
    numFramesLeft = numRequestedFrames;
    while(numFramesLeft > 0)
        
        % Compute the time when the first frame in the current batch
        % starts. The 0.5 below is added in order to hit inside the frame
        % and hopefully resolve any round off error confusion as to which
        % frame is meant as starting.
        startTime = (oneBeforeFirstInCurrBatch + 0.5)/frameRate;
        
        % Compute the number of frames in the current batch. 
        numFramesInCurrBatch = min(framesPerBatch, numFramesLeft);
        
        % Convert the video to a series of .pgm files stored on a disk.
        system(['ffmpeg -i "' movieFileName '" -ss ' num2str(startTime) ...
            ' -vframes ' num2str(numFramesInCurrBatch) ...
            ' -pix_fmt gray16be ' ffmpegImageFileNamePattern]);
        
        % Rename the image files just written with the actual frame indices
        % which could not have been generated directly with ffmpeg.
        for k = 1:numFramesInCurrBatch
            % Construct the name of a file to be renamed.
            ffmpegImageFileName = [imageFileNamePrefix 'Ffmpeg_' num2str(k, formatStr) '.' imageFileExt];
            % Construct the new name for the file to be renamed.
            tempImageFileName = [imageFileNamePrefix num2str(k + oneBeforeFirstInCurrBatch, formatStr) '.' imageFileExt];
            % Rename using a fast java line of code.
            java.io.File(ffmpegImageFileName).renameTo(java.io.File(tempImageFileName));
        end
        
        % Keep track of the index of the frame before the first frame in
        % the current batch.
        oneBeforeFirstInCurrBatch = oneBeforeFirstInCurrBatch + framesPerBatch;
        % Keep track of the number of frames from the current subrange that
        % are not processed yet.
        numFramesLeft = numFramesLeft - numFramesInCurrBatch;
    end
end

return;
% end of the function "getImagesFromMovie"
